---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
-- File: PacketParser - Packet Parser source file. 
--
-- Author: Andy Schneck, Justin Keefer 
--
-- Description: This file takes in the data from the RS232 port and extracts the 
--              necessary data from the packet header. Depending on the command,
--              (read/write/no-op) there will be a payload with a size specified
--              in the header. This payload contains the device address and the
--              data to write.
--
-- Inputs: data_in - 8 bit data from the RS232 port.
--         reset - Flag to reset the packet parser.
--         ready_in - Flag to determine if data from the RS232 port is ready.
--
-- Outputs: The packet header determines the following information:
--          address - [9:0] in the packet header. This specifies the read or write 
--                    address for the data.
--          command - [10] Either Write or Read in the packet header.
--                    A write command (active low) will write the payload to the 
--                    specified address provided in the header. 
--                    A read command (active high) will read from the specified 
--                    address provided in the header. 
--          payload_size - [14:11] in the packet header. This specifies the size of 
--                         the payload for a write command. 0000 for a payload size 
--                         represents a no-op command. 
--          data_out - The outgoing payload data for a write command.
--          ready_out - Flag for the memory controller for outgoing data that is ready.
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
-- library UNISIM;
-- use UNISIM.VComponents.all;

----------------------------------------------
-- entity declaration for the packet parser -- 
----------------------------------------------
entity PacketParser is
	port(
		data_in      : in std_logic_vector(7 downto 0); -- Input data (8 bits)
		reset        : in std_logic; -- Reset (active high) 
		ready_in     : in std_logic; -- Ready bit (active high) 
		address      : out std_logic_vector(9 downto 0); -- Address to write/read (10 bits) 
		payload_size : out std_logic_vector(3 downto 0); -- Size of payload in bytes (max 16 bytes)
		ready_out    : out std_logic; -- Ready flag (active high) 
		write_en     : out std_logic; -- Write enable flag (active low)
		read_en      : out std_logic; -- Read enable flag (active high)
		state_out    : out STd_logic_vector(1 downto 0);  -- For debugging purposes. displays the current state
		data_out     : out std_logic_vector(7 downto 0)); -- Output data (8 bits)
end PacketParser;

---------------------------------------------
-- Architecture body for the packet parser -- 
---------------------------------------------
architecture Behavioral of PacketParser is
	-- active states
	type state is (read_first_header_byte,  -- First state. Reads the first 8 bits of the packet header.
	               read_second_header_byte, -- Second state. Reads the second 8 bits of the packet header.
	               read_payload,            -- Third state. If the command is Write, this state reads the payload data.
	               no_op);                  -- No-Op state.
	signal byte_counter : integer := 1; -- Keeps track of the payload data byte count.
	signal current_state : state := read_first_header_byte; 
	signal next_state : state;
	signal temp_address : std_logic_vector(9 downto 0) := "0000000000";
begin 
	-- Combinational logic
	process(current_state, ready_in) 
	variable temp_size : std_logic_vector(3 downto 0) := "0000"; 
		begin 
		if( rising_edge(ready_in) ) then
			case current_state is 
				-- read the header
				when read_first_header_byte => 
					ready_out <= '0'; 

					-- Set the first 8 bits of the r/w addr
					temp_address(7 downto 0) <= data_in; 
					temp_address(9 downto 8) <= "00"; 
					next_state <= read_second_header_byte;
					state_out <= "01";

				when read_second_header_byte => 
					-- Set the rest of the r/w addr 
					temp_address(9 downto 8) <= data_in(1 downto 0); 

					-- set the payload size. 0000 is no-op
					temp_size := data_in(6 downto 3); 
					payload_size <= temp_size; 

					-- set the command (read or write)
					if(data_in(2) <= '0' and temp_size = "0000") then 
						write_en <= '1';
						read_en <= '0'; 
						next_state <= read_first_header_byte;
						state_out <= "00";
					elsif( data_in(2) = '0' ) then
						write_en <= '1';
						read_en <= '0'; 
						next_state <= read_payload;
						state_out <= "10";
					elsif(data_in(2) <= '1') then 
						read_en <= '1'; 
						write_en <= '0';
						address <= data_in(1 downto 0) & temp_address(7 downto 0);
						ready_out <= '1';
						next_state <= read_first_header_byte; 
						state_out <= "00";
					end if; 

				-- read the payload
				when read_payload =>
					if(temp_size >= byte_counter ) then
						data_out <= data_in;
						address <= temp_address; 
						ready_out <= '1'; -- send the header data / payload 
						temp_address <= temp_address + 1; 
						byte_counter <= byte_counter + 1; 
						next_state <= read_payload;
						state_out <= "10";
					elsif(temp_size < byte_counter) then
						ready_out <= '0'; -- payload is done being read
						byte_counter <= 1; 
						temp_address <= "0000000000"; 
						next_state <= read_first_header_byte;
						state_out <= "00";
					end if; 

				-- no-op operation
				when no_op => 
					next_state <= read_first_header_byte;
					state_out <= "00";
					byte_counter <= 1;
					address <= "0000000000";
					temp_address <= "0000000000";
					ready_out <= '0'; 

				-- default case
				when others =>
				--do nothing
				end case; 
		end if; 

		-- Drive ready_out low
		if(ready_in = '0') then
			ready_out <= '0';
		end if;
	end process; 

	-- Sequential logic
	process(reset, ready_in)
		begin 
			if( reset = '1') then
				current_state <= read_first_header_byte;
			else
				current_state <= next_state;
			end if; 
	end process;
end Behavioral;